
#include "unp.h"
#include <net/if.h>
#include <sys/ioctl.h>

const char * sock_ntop(struct sockaddr * sa, socklen_t sa_len) {

    static char buffer[132];
    bzero(buffer, sizeof(132));
    switch (sa_len)
    {
    case sizeof(struct sockaddr_in):{
        struct sockaddr_in * ptr = (struct sockaddr_in * )sa;
        inet_ntop(ptr->sin_family, &ptr->sin_addr, buffer, sizeof(buffer));
        break;
    }
    case sizeof(struct sockaddr_in6):

    default:
        break;
    }
    return buffer;
} 

int sockfd_to_family(int sockfd) {

    struct sockaddr_in addr;
    bzero(&addr, sizeof(addr));

    socklen_t len = sizeof(addr);
    int result = getsockname(sockfd, (struct sockaddr * )&addr, &len);

    if (result == 0) {

        return addr.sin_family;
    }
    // 日常返回小于0的值
    return (-1);
}

int family_to_level(int family) {

    switch (family)
    {
    case AF_INET:

        return IPPROTO_IP;
    case AF_INET6:

        return IPPROTO_IPV6;
    default:
        break;
    }
    return (-1);
}

int Udp_client(const char * name, uint16_t port, void ** addr, socklen_t * len) {

    char buffer[BUFFER_SIZE];
    bzero(buffer, sizeof(BUFFER_SIZE));

    struct hostent result_buffer;
    struct hostent *result;
    bzero(&result_buffer, sizeof(result));

    int errno_code = 0;
    int result_code = gethostbyname_r(name, &result_buffer, buffer, BUFFER_SIZE, &result, &errno_code);
    if (result_code != 0 || result == NULL) {

        *addr = NULL;
        *len  = 0;
        return -1;
    }

    struct sockaddr_in * addr_ptr = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
    bzero(addr_ptr, sizeof(addr_ptr));

    printf("sizeof(sockaddr_in) = %ld\n", sizeof(struct sockaddr_in));

    int sockfd = 0;
    char ** ptr = result_buffer.h_addr_list;
    for (; *ptr != NULL; ptr++) {

        sockfd = socket(result_buffer.h_addrtype, SOCK_DGRAM, IPPROTO_UDP);

        memcpy(&addr_ptr->sin_addr, *ptr, sizeof(addr_ptr->sin_addr));
        addr_ptr->sin_family = result_buffer.h_addrtype;
        addr_ptr->sin_port   = htons(port);

        if (connect(sockfd, (struct sockaddr *)addr_ptr, sizeof(struct sockaddr_in)) == 0) {

            *addr = addr_ptr;
            *len  = sizeof(*addr_ptr);
            break;
        } 
        else {

            sockfd = -1;
        }
    }
    return sockfd;
}

int mcast_join(int sockfd, const struct sockaddr * grp, socklen_t grplen, const char * ifname, u_int ifindex) {

#ifdef MCAST_JOIN_GROUP
    struct group_req req;

    if (ifindex > 0) {

        req.gr_interface = ifindex;
    } else if (ifname != NULL) {

        if ((req.gr_interface = if_nametoindex(ifname)) == 0) {

            errno = ENXIO;
            return (-1);
        }
    } else {

        req.gr_interface = 0;
    }
    if (grplen > sizeof(req.gr_group)) {

        errno = EINVAL;
        return (-1);
    }
    memcpy(&req.gr_group, grp, grplen);
    // 直接设置setsockopt
    return (setsockopt(sockfd, family_to_level(grp->sa_family), MCAST_JOIN_GROUP, &req, sizeof(req)));
#else
    switch (grp->sa_family)
    {
        case AF_INET:
        {
            struct ip_mreq mreq;
            struct ifreq ifreq;

            memcpy(&mreq.imr_multiaddr, &((const struct sockaddr_in * )grp)->sin_addr, sizeof(struct in_addr));

            if (ifindex > 0) {

                if (if_indextoname(ifindex) == NULL) {

                    errno = ENXIO;
                    return (-1);
                }
                goto doioctl;
            } else if (ifname != NULL) {

                strncpy(ifreq.ifr_name, ifname, IFNAMSIZ);
doioctl:
                if (ioctl(sockfd, SIOCGIFADDR, &ifreq) < 0) {

                    // failure
                    return (-1);
                }
                memcpy(&mreq.imr_interface, &((struct sockaddr_in * )&ifreq.ifr_addr)->sin_addr, sizeof(struct in_addr));
            } else {

                mreq.imr_interface.s_addr = htol(INADDR_ANY);
            }
            return (setsockopt(sockfd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, &mreq, sizeof(mreq)));
        }
#ifdef IPV6
        case AF_INET6: {

            struct ipv6_mreq mreq6;
            memcpy(&mreq6.ipv6_multiaddr, &((const struct sockaddr_in6 * )grp)->sin6_addr, sizeof(struct in6_addr));

            if (ifindex > 0) {

                mreq6.ipv6mr_interface = ifindex;
            } else if (ifname != NULL) {

                if ((mreq6.ipv6mr_interface = if_nametoindex(ifname)) == 0) {

                    errno = ENXIO;
                    return(-1);
                }
            } else {

                mreq6.ipv6mr_interface = 0;
            }
            return setsockopt(sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6));
        }
#endif
        default:
            errno = EAFNOSUPPORT;
            return (-1);
    }
#endif
}


int mcast_leave(int sockfd, const struct sockaddr * grp, socklen_t grplen) {

#ifdef MCAST_LEAVE_GROUP
    struct group_req req;

#endif
}

int mcast_set_loop(int sockfd, int onoff) {

    switch(sockfd_to_family(sockfd)) {

        case AF_INET: {

            u_char flag;
            flag = onoff;
            return setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &flag, sizeof(flag));
        }
#ifdef IPV6
        case AF_INET6: {

            u_int   flag;
            flag = onoff;
            return setsockopt(sockfd, IPPROTO_IPV6, IP_MULTICAST_LOOP, &flag, sizeof(flag));
        }
#endif
        default: {

            errno = EAFNOSUPPORT;
            return(-1);
        }
    }
}
